package edu.jhu.cs.oose.scheduleit.vo;

import java.util.LinkedList;
import java.util.List;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.OneToOne;
import javax.persistence.Transient;
import org.hibernate.annotations.CollectionId;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Type;

import edu.jhu.cs.oose.scheduleit.util.FilterParser;
import edu.jhu.cs.oose.scheduleit.util.ScheduleType;

/**
 * Entity class of Schedule
 * @author oose_group
 *
 */
@Entity
public class Schedule implements Comparable<Schedule> {

	/**
	 * The identifier of schedule
	 */
	@Id
	@GeneratedValue
	private int id;

	/**
	 * The host of this schedule
	 */
	@OneToOne
	private UserInfo host;

	/**
	 * The group to which this schedule may be belonged
	 */
	@OneToOne
	private Group group;

	/**
	 * Whether this schedule can be edited by user other than its host
	 */
	@Basic
	private boolean editable;

	/**
	 * The topic of this schedule
	 */
	@Basic
	@Column(length = 2048)
	private String topic;

	/**
	 * The tags of this schedule
	 */
	@Basic
	@Column(length = 2048)
	private String tags;

	/**
	 * How many times this schedule has been clicked
	 */
	@Basic
	private long clicks;

	/**
	 * Whether this schedule is public
	 */
	@Basic
	private boolean publicAccess;

	/**
	 * The source schedules if this schedule is merged by other schedules
	 */
	@ManyToMany(fetch = FetchType.EAGER)
	@GenericGenerator(name="uuid-gen", strategy = "uuid")
    @CollectionId(columns = @Column(name = "COL_ID"), type = @Type(type = "string"), generator = "uuid-gen") 
	private List<Schedule> mergedSchedules;

	/**
	 * The events belongs to this schedule
	 */
	@SuppressWarnings("unused")
	@ManyToMany(fetch = FetchType.LAZY)
	private List<Event> events;

	/**
	 * The type of this schedule
	 */
	@Transient
	private ScheduleType type;
	
	/**
	 * The selected status of this schedule
	 */
	@Transient
	private boolean selected;
	
	/**
	 * The getter of id
	 * @return
	 */
	public int getId() {
		return id;
	}

	/**
	 * The setter of id
	 * @param id
	 */
	public void setId(int id) {
		this.id = id;
	}

	public UserInfo getHost() {
		return host;
	}

	/**
	 * The setter of host
	 * @param host
	 */
	public void setHost(UserInfo host) {
		this.host = host;
	}

	/**
	 * The getter of editable
	 * @return
	 */
	public boolean isEditable() {
		return editable;
	}

	/**
	 * The setter of editable
	 * @param editable
	 */
	public void setEditable(boolean editable) {
		this.editable = editable;
	}

	/**
	 * The getter of topic
	 * @return
	 */
	public String getTopic() {
		return FilterParser.translateBeforeTopicRead(this.topic);		
	}

	/**
	 * The setter of topic
	 * @param topic
	 */
	public void setTopic(String topic) {
		this.topic = FilterParser.translateBeforeTopicWrite(topic);
	}

	/**
	 * The getter of tags
	 * @return
	 */
	public List<String> getTags() {
		return FilterParser.translateBeforeTagRead(this.tags);
	}

	/**
	 * The setter of tags
	 * @param tags
	 */
	public void setTags(List<String> tags) {
		this.tags = FilterParser.translateBeforeTagWrite(tags);
	}

	/**
	 * The getter of clicks
	 */
	public long getClicks() {
		return clicks;
	}

	/**
	 * The setter of clicks
	 * @param clicks
	 */
	public void setClicks(long clicks) {
		this.clicks = clicks;
	}

	/**
	 * The getter of publicAccess
	 * @return
	 */
	public boolean isPublicAccess() {
		return publicAccess;
	}

	/**
	 * The setter of publicAccess
	 * @param publicAccess
	 */
	public void setPublicAccess(boolean publicAccess) {
		this.publicAccess = publicAccess;
	}
	
	/**
	 * The getter of group
	 */
	public Group getGroup() {
		return group;
	}

	/**
	 * The setter of group
	 * @param group
	 */
	public void setGroup(Group group) {
		this.group = group;
	}

	/**
	 * The getter of mergedSchedules
	 * @return
	 */
	public List<Schedule> getMergedSchedules() {
		if (this.mergedSchedules == null) {
			this.mergedSchedules = new LinkedList<Schedule>();
		}
		return mergedSchedules;
	}
	
	/**
	 * Add a source schedule for a merged schedule
	 * @param schedule	The schedule going to be merged
	 * @return	Whether this operation successes
	 */
	public boolean addMergedSchedule(Schedule schedule) {
		if (this.mergedSchedules == null) {
			this.mergedSchedules = new LinkedList<Schedule>();
		}
		return this.mergedSchedules.add(schedule);
	}

	/**
	 * Remove a source schedule for a merged schedule
	 * @param schedule	The schedule going to be removed from the merged schedule
	 * @return	Whether this operation successes
	 */
	public boolean removeMergedSchedule(Schedule schedule) {
		return this.mergedSchedules == null ? false : this.mergedSchedules.remove(schedule);
	}
	
	/**
	 * Remove all of the source schedules for a merged schedule
	 */
	public void removeAllMergedSchedules(){
		if (this.mergedSchedules != null){
			this.mergedSchedules.clear();
		}
	}
	
	/**
	 * The getter of type
	 * @return
	 */
	public ScheduleType getType() {
		return type;
	}
	
	/**
	 * The getter of selected
	 * @return
	 */
	public boolean isSelected() {
		return selected;
	}
	
	/**
	 * The setter of selected
	 * @param selected
	 */
	public void setSelected(boolean selected) {
		this.selected = selected;
	}

	/**
	 * The setter of type
	 * @param type
	 */
	public void setType(ScheduleType type) {
		this.type = type;
	}
	
	@Override
	public int compareTo(Schedule schedule) {
		if (this.getId() == schedule.getId()) {
			return 0;
		} else if (this.getId() == 0) {
			return -1;
		} else if (schedule.getId() == 0) {
			return 1;
		} else {
			return this.getId() > schedule.getId() ? 1 : -1;
		}
	}

	@Override
	public boolean equals(Object obj) {
		if (obj instanceof Schedule){
			return this.getId() == ((Schedule)obj).getId();
		}
		return false;
	}	
	
}
